function out = parse_defaults_optionvaluepairs( defaults , varargin )
% out = parse_defaults_optionvaluepairs( defaults , option1, value1, ...)
% out = parse_defaults_optionvaluepairs( defaults , option_value_struct)
% Helper function to parse option value pairs and update the fields in the defaults structure.
%
% INPUTS:
% defaults : structure with default values for all possible options.
% option_i : string with name of options, case sensitive matched to the fields in 
%            defaults. Errors if the option is not found in the defaults structure.
%            For scalar structures in defaults, the subfields can be 
%            assigned by specifying 'option.sub_field_name' as option_i.
% value_i  : anything (that can be assigned to a field of a structure).
% option_value_struct : structure with valid fields (each field should also be in defaults)
%            The fields override the default values.
% 
% OUTPUTS:
% out      : structure defaults, with all fields specified by the options 
%            replaced by the corresponding values.
%            No checking of the values is performed.
%
% Created by Dirk Poot (d.poot@erasmusmc.nl), Erasmus medical center, Rotterdam

out = defaults;
if mod(nargin,2)~=1
    % if not odd number of input arguments => no well defined option-value pairs; maybe option_value_struct 
    if nargin==2 && isstruct(varargin{1}) && numel(varargin{1})==numel(defaults)
        % option, values are already in structure.
        fieldnm = fieldnames(varargin{1});
        for k=1:numel(fieldnm)
            if ~isfield(out,fieldnm{k})
                error('Each field of your option structure should identify a valid option.');
            end;
            if isstruct(out.(fieldnm{k})) && isstruct(varargin{1}.(fieldnm{k}))
                out.(fieldnm{k}) = parse_defaults_optionvaluepairs( out.(fieldnm{k}) , varargin{1}.(fieldnm{k}) );
            else
                out.(fieldnm{k}) = varargin{1}.(fieldnm{k});
            end;
        end;
        return;
    else
        error('arguments should come in option+value pairs.');
    end;
end;
npairs = (nargin-1)/2;
isopt = [true(1,npairs);false(1,npairs)];
for k=1:npairs
    if ~ischar(varargin{k*2-1}) 
        isopt(1,k) = false;
        break;
    end;
end;
[allfieldnm , nnormal] = expandedfieldnames(defaults);

[isopt(isopt), idx ] = ismember( varargin(isopt), allfieldnm);
haserror = ~all(isopt(1,:));
if haserror
    allfieldsstr = sprintf('"%s", ', allfieldnm{:});
    alloptsstr = cell(1,npairs);
    for k2=find(~isopt(1,:)) %1:npairs
        if ischar(varargin{k2*2-1})
            alloptsstr{k2} = ['"' varargin{k2*2-1} '", '];
        else
            alloptsstr{k2} = sprintf('class of option %d: %s, ',k2, class(varargin{k2*2-1}));
        end;
    end;
    alloptsstr= [alloptsstr{:}];
%     error('Each option should be a string identifying a valid option.\nAll valid options: %s\nProvided options : %s', allfieldsstr(1:end-2), alloptsstr(1:end-2));
    error('Each option should be a string identifying a valid option.\nAll valid options: %s\nProvided invalid options : %s', allfieldsstr(1:end-2), alloptsstr(1:end-2));
else
    for k=1:numel(idx)
        if idx(k) <= nnormal
            out.( allfieldnm{ idx(k) } ) = varargin{k*2};
        else
            eval( ['out.' allfieldnm{ idx(k) } '=varargin{k*2};']);
        end;
    end;
end;


function [list, norig] = expandedfieldnames( s )
list = fieldnames( s );
norig = numel(list);
adlist = {};
for k=1:norig
    if isstruct(s.(list{k})) && numel(s.(list{k}))==1
        adlist{end+1} = expandedfieldnames( s.(list{k}) );
        for k2 = 1:numel(adlist{end})
            adlist{end}{k2} = [list{k} '.' adlist{end}{k2}];
        end;
    end;
end;
if ~isempty(adlist)
    list = vertcat(list,adlist{:});
end;